﻿/*
 * Copyright 2012 Rene Prost, Registrite ja Infosüsteemide Keskus 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Threading;
using Xtee.Core.Strings;

namespace Xtee.Core
{
    public class MessagePathStructure
    {
        private readonly string _root;
        private readonly string _type;
        private string _client;
        private readonly string _personalCode;
        private readonly string _service; 
        private readonly DateTime _timeOfDay;
        private string _currentFormat;
        private string _fullPath;
        public const string DefaultFormatForServer =@"[service]\[year]\[month]\[day]\[personalcode]\[year]-[month]-[day]__[timeofday]__[ms]@[client].[type].txt";
        public const string DefaultFormatForClient = @"[service]\[year]\[month]\[day]\[personalcode]\[year]-[month]-[day]__[timeofday]__[ms].[type].txt";
        private readonly IList<string> _requiredKeyWords = new List<string> { "year","month","day","timeofday","ms","type" };
        private static readonly PreciseTimeOfDay PreciseTimeOfDay = new PreciseTimeOfDay();

        public MessagePathStructure(string root, bool isForRequest, string client, string personalCode, string service) : this(root, isForRequest, client, personalCode, service, PreciseTimeOfDay)
        {
            
        }
        public MessagePathStructure(string root, bool isForRequest, string client, string personalCode, string service, IPreciceTimeOfDay timeOfDay)
        {
            _root = root;
            _type = (isForRequest ? "request" : "response");
            _client = client;
            MakeClientFileNameFriendly();
            _personalCode = personalCode;
            if (!string.IsNullOrEmpty(_personalCode))
            {
                if (_personalCode.StartsWith("EE"))
                {
                    _personalCode = _personalCode.Substring(2);
                } 
            }
            _service = service;
            _timeOfDay = timeOfDay.Resolve();  
        }

        private void MakeClientFileNameFriendly()
        {
            if (!string.IsNullOrEmpty(_client))
            {
                _client = (_client == "::1" ? "localhost" : _client); 
                IPAddress address; 
                if (IPAddress.TryParse(_client, out address) && address.AddressFamily == System.Net.Sockets.AddressFamily.InterNetworkV6)
                {
                    _client = _client.Replace(":", "_");
                } 
            }
        }
         
        public MessagePathStructure EnsureFolder()
        {
            var filepath = new FileInfo(FullPath);
            if (!Directory.Exists(filepath.DirectoryName))
            {
                Directory.CreateDirectory(filepath.DirectoryName);
            } 
            return this;
        }

        
        public string FullPath
        {
            get
            {
                if (string.IsNullOrEmpty(_fullPath))
                {
                    _fullPath = ApplyFormat();
                }
                return _fullPath;
            }
        }

        public string PersonalCode
        {
            get { return _personalCode; }
        }

        public int Year
        {
            get
            {
                return _timeOfDay.Year;
            }
        }
        public int Month
        {
            get
            {
                return _timeOfDay.Month;
            }
        }
        public int Day
        {
            get
            {
                return _timeOfDay.Day;
            }
        }

        public string Service
        {
            get { return _service; }
        }

        public string TimeOfDay
        {
            get
            {
                return _timeOfDay.ToString("HH-mm-ss");
            }
        }

        public string Ms
        {
            get
            {
                return _timeOfDay.ToString("ffffff");
            }
        }

        public string Client
        {
            get { return _client; }
        }

        public string Type
        {
            get { return _type; }
        }
         
        public MessagePathStructure SetFormat(string format)
        {
            _currentFormat = format;
            _fullPath = null;
            return this;
        }

        private string ApplyFormat()
        {
            if (string.IsNullOrEmpty(_currentFormat))
            {
                _currentFormat = string.IsNullOrEmpty(_client)?DefaultFormatForClient : DefaultFormatForServer;
            }
            var parts = _currentFormat.Split(new[] { @"\", @"/" }, StringSplitOptions.RemoveEmptyEntries);
            try
            {
                var formatters = parts.
                    Select(x => new TokenFormatter(x)).
                    ToList();
                var result = formatters.
                    Select(x => x.Inject(this)).
                    Where(x=>!string.IsNullOrEmpty(x)).
                    Aggregate(_root, Path.Combine);
                 
                if (!_requiredKeyWords.All(formatters.SelectMany(x => x.KeywordsUsed).Contains))
                {
                    throw new MessagePathStructureInvalid(
                        "[year][month][day][timeofday][ms] and [type] is required to specify in format to ensure uniquness!");
                }
                return result;
            }
            catch (FormatException)
            {
                throw new MessagePathStructureInvalid("Message path structure format is not valid!");
            }
        }
         
    }
}